package com.bawei.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.bawei.common.core.domain.R;
import com.bawei.common.core.exception.ServiceException;
import com.bawei.common.core.utils.StringUtils;
import com.bawei.es.service.EsIndexService;
import com.bawei.system.domain.dto.EsDocInsertDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.settings.Settings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Service
@Slf4j
public class EsIndexServiceImpl implements EsIndexService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public void indexCreates(EsDocInsertDto esDocInsertDto) {
        //如果索引为空 抛出异常
        if (StringUtils.isEmpty(esDocInsertDto.getIndexName())) {
            throw new ServiceException("索引名称不能为空");
        }
        CreateIndexRequest request = new CreateIndexRequest(esDocInsertDto.getIndexName());
        if (esDocInsertDto.getSettings() != null) {
            request.settings(esDocInsertDto.getSettings());
        }
        try {
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("连接ES服务异常:{}", e);
            throw new RuntimeException("创建索引失败，请联系管理员或查看服务日志");
        }
    }

    @Override
    public void indexCreate(String indexName) {


// 发送请求以更新映射
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("索引名称不能为空");
        }
        PutMappingRequest putMappingRequest = new PutMappingRequest(indexName);
        Map<String, Object> jsonMap = new HashMap<>();
        Map<String, Object> properties = new HashMap<>();
        Map<String, Object> completion = new HashMap<>();
        completion.put("type", "completion"); // 设置 completion 类型
        properties.put("keywords", completion); // 将 completion 映射到 keywords 字段
        jsonMap.put("properties", properties);
        putMappingRequest.source(jsonMap);
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        try {
            AcknowledgedResponse putMappingResponse = restHighLevelClient.indices().putMapping(putMappingRequest, RequestOptions.DEFAULT);        } catch (IOException e) {
            log.error("连接ES服务异常:{}", e);
            throw new RuntimeException("创建索引失败，请联系管理员或查看服务日志");
        }
    }


    @Override
    public Boolean indexExit(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空");
        }
        GetIndexRequest indexRequest = new GetIndexRequest(indexName);
        try {
            return restHighLevelClient.indices().exists(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void indexDelete(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空 ");
        }
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        try {
            AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("删除索引失败：{}", e);
            throw new RuntimeException("调用es服务删除索引失败，详情请查看日志");
        }
    }

    @Override
    public void indexCreateWithMapping(EsDocInsertDto esDocInsertDto) {
        if (StringUtils.isEmpty(esDocInsertDto.getIndexName())) {
            throw new ServiceException("indexName不能为空");
        }

        // 判断索引是否已经存在,不存在则创建索引
        if (!indexExit(esDocInsertDto.getIndexName())) {

            CreateIndexRequest request = new CreateIndexRequest(esDocInsertDto.getIndexName());
            if (ObjectUtils.isNotEmpty(esDocInsertDto.getSettings())) {
                request.settings(esDocInsertDto.getSettings());
            }
            if (ObjectUtils.isNotEmpty(esDocInsertDto.getMappings())) {
                request.mapping(esDocInsertDto.getMappings());
            }

            try {
                restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            } catch (Exception e) {
                log.error("调用es创建索引异常：{}", e);
                throw new RuntimeException("创建索引失败");
            }

        }

    }

    @Override
    public void indexGetMapping(String indexName) {
        if (StringUtils.isEmpty(indexName)){
            throw new ServiceException("indexName不能为空");
        }
        GetMappingsRequest request = new GetMappingsRequest().indices(indexName);
        try {
            GetMappingsResponse response = restHighLevelClient.indices().getMapping(request, RequestOptions.DEFAULT);
            Map<String, MappingMetaData> mappings = response.mappings();
            MappingMetaData mappingMetaData = mappings.get(indexName);
            Map<String, Object> stringObjectMap = mappingMetaData.sourceAsMap();
        } catch (IOException e) {
            log.error("根据索引名称获取Mapping异常:{}",e);
            throw new RuntimeException("根据索引名称获取Mapping异常");
        }
    }


    @Override
    public EsDocInsertDto indexDetails(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空");
        }

        GetMappingsRequest mappingsRequest = new GetMappingsRequest().indices(indexName);
        GetSettingsRequest settingsRequest = new GetSettingsRequest().indices(indexName);

        EsDocInsertDto index = new EsDocInsertDto();
        try {
            // 获取mapping
            GetMappingsResponse mappingsResponse = restHighLevelClient.indices().getMapping(mappingsRequest, RequestOptions.DEFAULT);
            Map<String, MappingMetaData> mappings = mappingsResponse.mappings();
            MappingMetaData mappingMetaData = mappings.get(indexName);
            Map<String, Object> MappingsMap = mappingMetaData.sourceAsMap();

            // 获取setting
            GetSettingsResponse settingsResponse = restHighLevelClient.indices().getSettings(settingsRequest, RequestOptions.DEFAULT);
            Settings settings = settingsResponse.getIndexToSettings().get(indexName);
            HashMap<String, Object> settingsMap = new HashMap<>();
            if (settings != null){
                settings.keySet().forEach(settingKey -> {
                    String settingValue = settings.get(settingKey);
                    settingsMap.put(settingKey, settingValue);
                });
            }

            index.setIndexName(indexName);
            index.setMappings(MappingsMap);
            index.setSettings(settingsMap);
            return index;
        } catch (Exception e) {
            log.error("根据索引名称获取索引信息异常:{}", e);
            throw new RuntimeException("根据索引名称获取Mapping异常");
        }
    }

    @Override
    public void indexUpdate(EsDocInsertDto esDocInsertDto,Integer flag) {
        if (StringUtils.isEmpty(esDocInsertDto.getIndexName())){
            throw new ServiceException("indexName不能为空");
        }

        if (esDocInsertDto.getMappings().isEmpty() && esDocInsertDto.getSettings().isEmpty()) {
            throw new RuntimeException("请至少更新映射或配置之一");
        }

        // 根据索引名称查询mapping映射
        EsDocInsertDto oldIndex = indexDetails(esDocInsertDto.getIndexName());

        // 从传入的参数中取出 mapping 的 properties
        Map requestProperties = (Map) esDocInsertDto.getMappings().get("properties");
        Map properties = new HashMap();
        if (requestProperties != null) {
            // 根据索引名取出 索引原本存在的 mapping 的 properties
            properties = (Map) oldIndex.getMappings().get("properties");
        }

        // 从传入的参数中取出 setting
        Map requestSettings = esDocInsertDto.getSettings();
        Map settings = new HashMap();
        if (requestSettings != null) {
            // 根据索引名取出 索引原本存在的 setting
            settings = oldIndex.getSettings();
        }

        // 传来一个标记，如果为 1 代表要删除字段映射 否则 代表修改或添加映射
        if (flag == 1) {
            // 获取请求发送的映射的所有字段名
            Set mappingsKey = requestProperties.keySet();
            // 从原本的映射中移除传入的字段名
            properties.keySet().removeAll(mappingsKey);

            // 获取请求发送的配置的所有配置名
            Set settingsKey = requestSettings.keySet();
            // 从原本的配置中移除传入的配置名
            settings.keySet().removeAll(settingsKey);
        }

        if (requestProperties != null) {
            // 重新给properties赋值
            esDocInsertDto.getMappings().put("properties", properties);
            // 将传入的properties字段名放入 根据索引名称查询到的 properties 中 (如果已有该字段，则会覆盖原先的值，以此来达到修改的效果)
            properties.putAll(requestProperties);
        }

        if (requestSettings != null) {
            // 给settings赋值
            esDocInsertDto.getSettings().putAll(requestSettings);
        }

        // es不支持直接删除映射，所以需要删除索引重新创建索引映射
        indexDelete(esDocInsertDto.getIndexName());

        try {
            // 重新创建索引
            indexCreateWithMapping(esDocInsertDto);
        } catch (Exception e) {
            // 报错后重新按原本的索引配置创建索引，避免到最后一步报错导致原本索引被删除无法恢复
            indexCreateWithMapping(oldIndex);
            throw new RuntimeException(e);
        }
    }

}
